<?php
/**
 * IoC容器（服务容器）
 *
 * 只需要向容器提交一个脚本，工厂就能够通过脚本自动化生产
 */
class Container
{
    //绑定的方法
    protected $binds;
    //绑定的实例
    protected $instances;
    //
    public function bind($abstract,$concrete)
    {
        //如果传入的是一个匿名类，则将它放入绑定对象。
        if($concrete instanceof Closure){
            $this->binds[$abstract] = $concrete;
        }
        //否则将其放入实例
        else{
            $this->instances[$abstract] = $concrete;
        }
    }
    //生产方法
    public function make($abstract,$parameters = [])
    {
        if(!is_array($parameters)){
            $parameters = (array)$parameters;
        }
        //如果存在具体的实例，返回该实例
        if(isset($this->instances[$abstract])){
            return $this->instances[$abstract];
        }

        array_unshift($parameters,$this);
        return call_user_func_array($this->binds[$abstract],$parameters);
    }
}

//能力抽象类
abstract class Power{
    //说出能力
    abstract public function say();
}
//一些例子
class Superman
{
    protected $power;
    public function __construct(Power $power)
    {
        $this->power = $power;
    }
    public function say()
    {
        $power = $this->power->say();
        echo "I have $power".PHP_EOL;
    }
}

class XPower extends Power
{
    public function say()
    {
        return "XPower";
    }
}
class UltraBomb extends Power
{
    public function say()
    {
        return "UltraBomb";
    }
}


//创建一个容器（后面称作超级工厂）
$container = new Container;
//向该超级工厂 添加超人的生产脚本
$container->bind('superman',function($container,$moduleName){
    return new Superman($container->make($moduleName));
});
// 向该 超级工厂 添加 超能力模组 的生产脚本
$container->bind('xpower', function($container) {
    return new XPower;
});
// 同上
$container->bind('ultrabomb', function($container) {
    return new UltraBomb;
});

$superman_1 = $container->make('superman', 'xpower');
$superman_2 = $container->make('superman', 'ultrabomb');
$superman_3 = $container->make('superman', 'xpower');

$superman_1->say();
$superman_2->say();
$superman_3->say();